/*
 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
 * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/statfs.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include "fs/dirent_fs.h"
#include "fs/fs.h"
#include "internal.h"
#include "proc_file.h"
#include "los_process.h"
#include "los_process_pri.h"
#include "los_container_bundle.h"
#ifdef LOSCFG_MNT_CONTAINER
#include "los_mnt_container.h"
#endif
#include "los_uts_container.h"
#include "los_user_container.h"
#include "los_ipc_container.h"
#include "proc_fs.h"
#include "los_vm_dump.h"
#ifdef LOSCFG_NET_CONTAINER
#include "los_net_container.h"
#endif
#include "path_cache.h"
#ifdef LOSCFG_TIME_CONTAINER
#include "los_time_container.h"
#endif


struct ProcfsPidEntry {
    char *name;
    unsigned int len;
    mode_t mode;
    struct VnodeOps *vop;
    struct ProcFileOperations *pfop;
};

#define PID_DIR_MODE (FMODE_DIRDEL | S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
#define PID_FILE_MODE (S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO)
#define PID_LINK_MODE (S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO)
#define PID_NAME_LEN 5
#define CONTAINER_NAME_LEN 40
#define FD_NAME_LEN 5
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(array)    (sizeof(array) / sizeof(array[0]))
#endif
#define CONTAINER(NAME) {       \
    .name = NAME,               \
    .len = sizeof(NAME) - 1,    \
    .mode = PID_LINK_MODE,      \
    .vop = &g_procfsContainerSubVops  \
}

#define PID(NAME, MODE, VOP, PFOP) { \
    .name = NAME,                   \
    .len = sizeof(NAME) - 1,        \
    .mode = MODE,                   \
    .vop = VOP,                     \
    .pfop = PFOP                    \
}

extern int VfsProcfsLookup(struct Vnode *parent, const char *name, int len, struct Vnode **vpp);
extern int VfsProcfsStat(struct Vnode *node, struct stat *buf);
extern int VfsProcfsReaddir(struct Vnode *node, struct fs_dirent_s *dir);
extern int VfsProcfsOpendir(struct Vnode *node,  struct fs_dirent_s *dir);
extern int VfsProcfsClosedir(struct Vnode *vp, struct fs_dirent_s *dir);
extern int VfsProcfsTruncate(struct Vnode *pVnode, off_t len);

extern int FspPIDMeminfoProcRead(struct SeqBuf *seqBuf, void *buf);
extern int FspPIDCpupProcRead(struct SeqBuf *seqBuf, void *buf);

extern struct ProcDirEntry *VnodeToEntry(struct Vnode *node);
static struct VnodeOps g_procfsContainerVops;
static struct VnodeOps g_procfsContainerSubVops;
#ifdef LOSCFG_USER_CONTAINER
static struct VnodeOps g_procfsComVops;
#endif
static struct VnodeOps g_procfsPidMenInfoRead;
static struct VnodeOps g_procfsPidCpupRead;
static struct VnodeOps g_procfsFdVops;
static struct VnodeOps g_procfsFdSubVops;
static struct VnodeOps g_procfsPidRootVops;
#ifdef LOSCFG_TIME_CONTAINER
static struct VnodeOps g_procfsPidTimeOffsetsVops;
#endif

int PIDMemInfoProcRead(struct SeqBuf *seqBuf, void *v)
{
#ifndef LOSCFG_KERNEL_VM
    return 0;
#endif

    char* name = (char*)v;
    if (name == NULL) return 0;
    UINT32 pid = atoi(name);
    LosProcessCB *pcb = OS_PCB_FROM_PID(pid);
    if (pcb == NULL)
    {
        PRINT_ERR("current process not exists!\n");
        return -get_errno();
    }
    LosVmSpace *vmSpace = pcb->vmSpace;
    if (vmSpace == NULL)
    {
        PRINT_ERR("meminfo not init failed!\n");
        return -get_errno();
    }

    (void)LosBufPrintf(seqBuf, "Name                     init\n");
    (void)LosBufPrintf(seqBuf, "VMSpaceSize:             %ld KB\n", vmSpace->size);
    (void)LosBufPrintf(seqBuf, "VMRegionSize:            %ld KB\n", vmSpace->heap->range.size);
    (void)LosBufPrintf(seqBuf, "RegionFlags:             %s\n", OsGetRegionNameOrFilePath(vmSpace->heap));
    (void)LosBufPrintf(seqBuf, "ShmidAboutSharedRegion:  %d\n", vmSpace->heap->shmid);
    (void)LosBufPrintf(seqBuf, "VMSpaceRorkFlags:        %d\n", vmSpace->heap->forkFlags);
    (void)LosBufPrintf(seqBuf, "VMRegionRype:            %d\n", vmSpace->heap->regionType);
    (void)LosBufPrintf(seqBuf, "VMSpaceMappingAreaSize:  %ld KB\n", vmSpace->mapSize);
    (void)LosBufPrintf(seqBuf, "TLB Asid:                %ld\n", vmSpace->archMmu.asid);
    (void)LosBufPrintf(seqBuf, "VMLck:                   %ld\n", vmSpace->lockedPageCount);
    return 0;
}

int PIDCpupProcRead(struct SeqBuf *seqBuf, void *v)
{
#ifndef LOSCFG_KERNEL_CPUP
    return 0;
#endif

    char* name = (char*)v;
    if (name == NULL) return 0;
    UINT32 pid = atoi(name);

    LosProcessCB *pcb = OS_PCB_FROM_PID(pid);
    if (pcb == NULL)
    {
        PRINT_ERR("current process not exists!\n");
        return -get_errno();
    }
    OsCpupBase *processCpup = pcb->processCpup;
    if (processCpup == NULL)
    {
        PRINT_ERR("cpup use not init failed!\n");
        return -get_errno();
    }
    (void)LosBufPrintf(seqBuf, "Name                     init\n");
    (void)LosBufPrintf(seqBuf, "TotalRunningTime:        %ld\n", processCpup->allTime);
    (void)LosBufPrintf(seqBuf, "StartTime:               %ld\n", processCpup->startTime);
    (void)LosBufPrintf(seqBuf, "HistoricalRunningRime:   ");
    for(UINT32 i = 0; i < OS_CPUP_HISTORY_RECORD_NUM + 1; i++)
        (void)LosBufPrintf(seqBuf, "%d  ", processCpup->historyTime[i]);
    (void)LosBufPrintf(seqBuf, "\n");
    return 0;
}

static struct ProcFileOperations PROC_PID_MEMINFO_READ = {
    .read = PIDMemInfoProcRead,
};

static struct ProcFileOperations PROC_PID_CPUP_READ = {
    .read = PIDCpupProcRead,
};


static int ProcfsCreatePidEntry(struct ProcDirEntry *parent, unsigned int pid)
{
    struct ProcDirEntry *sub = NULL;
    struct ProcDirEntry *pn = NULL;
    char name[PID_NAME_LEN + 1] = {0};

    sub = parent->subdir;
    snprintf(name, sizeof(name), "%u", pid);

    while (1)
    {
        // create ProcDirEntry if not found in subdirs.
        if (sub == NULL) {
            pn = ProcCreateData(name, PID_DIR_MODE, parent, parent->procFileOps, (void*)pid);
            if (pn == NULL) {
                PRINT_ERR("Create ProcDirEntry failed\n");
                return -ENODATA;
            }
            // set the current pointer to head because new ProcDirEntry are added to head.
            parent->pdirCurrent = parent->subdir;
            parent->pf->fPos = 0;
            break;
        }
        // exit while loop when the pidname is already exists.
        if (ProcMatch(strlen(name), name, sub)) {
            break;
        }
        sub = sub->next;
    }

    return LOS_OK;
}

static int ProcfsCreatePidSubEntry(struct ProcDirEntry *parent, const char* name, mode_t mode,
    const struct ProcFileOperations *procFileOps)
{
    struct ProcDirEntry *sub = NULL;
    struct ProcDirEntry *pn = NULL;

    sub = parent->subdir;

    while (1)
    {
        // create ProcDirEntry if not found in subdirs.
        if (sub == NULL) {
            pn = ProcCreateData(name, mode, parent, procFileOps, parent->name);
            if (pn == NULL) {
                PRINT_ERR("Create ProcDirEntry failed\n");
                return -ENODATA;
            }

            // set the current pointer to head because new ProcDirEntry are added to head.
            parent->pdirCurrent = parent->subdir;
            parent->pf->fPos = 0;
            break;
        }
        // exit while loop when the pidname is already exists.
        if (ProcMatch(strlen(name), name, sub)) {
            break;
        }
        sub = sub->next;
    }

    return LOS_OK;
}

static int ProcfsCreatePidEntries(struct Vnode *parent)
{
    int pidNum;
    unsigned int pidMaxNum;
    unsigned int *pidList = NULL;
    int result = 0;

    struct ProcDirEntry *entry = (struct ProcDirEntry *)(parent->data);
    if (entry == NULL) {
        return -ENODATA;
    }

    pidMaxNum = LOS_GetSystemProcessMaximum();
    pidList = (unsigned int *)malloc(pidMaxNum * sizeof(unsigned int));
    if (pidList == NULL) {
        return -ENOMEM;
    }
    pidNum = LOS_GetUsedPIDList(pidList, pidMaxNum);

    for (int i = 0; i < pidNum; i++) {
#ifdef LOSCFG_PID_CONTAINER
        UINT32 vpid = GetCurrPidByGlobalPid(pidList[i]);
        if (vpid == -LOS_NOK) {
            continue;
        }
        result = ProcfsCreatePidEntry(entry, vpid);
#else
        result = ProcfsCreatePidEntry(entry, pidList[i]);
#endif
        if (result != LOS_OK) {
            free(pidList);
            return result;
        }
    }

    free(pidList);
    return LOS_OK;
}

static void ProcfsFreePidVnode(const char *name, struct Vnode *parent)
{
    int ret = 0;
    struct Vnode *currVnode = NULL;

    VnodeHold();
    ret = PathCacheLookup(parent, name, strlen(name), &currVnode);
    VnodeDrop();
    if (ret == LOS_OK) {
        (void)VnodeFree(currVnode);
    }

    return;
}

static int ProcfsRemovePidEntries(struct Vnode *parent)
{
    struct ProcDirEntry *temp = NULL;
    struct ProcDirEntry *pde = NULL;
    char name[NAME_MAX] = {'\0'};

    struct ProcDirEntry *entry = (struct ProcDirEntry *)(parent->data);
    if (entry == NULL) {
        return -ENODATA;
    }

    pde = entry->subdir;
    // exit while loop when all subdir are checked.
    while (pde != NULL) {
        // do not check it when it is not a pid.
        if ((atoi(pde->name) <= 0) || (LOS_IsUsedPid(pde->name))) {
            pde = pde->next;
            continue;
        }

        temp = pde->next;
        strcpy_s(name, sizeof(name), pde->name);
        (void)RemoveProcEntry(name, entry);
        ProcfsFreePidVnode(name, parent);

        entry->pdirCurrent = entry->subdir;
        entry->pf->fPos = 0;
        pde = temp;
    }

    return LOS_OK;
}

int ProcfsCheckPid(struct Vnode *parent)
{
    int result = 0;

    result = ProcfsCreatePidEntries(parent);
    if (result != LOS_OK) {
        return result;
    }

    result = ProcfsRemovePidEntries(parent);
    if (result != LOS_OK) {
        return result;
    }

    return LOS_OK;
}

static const struct ProcfsPidEntry g_procfsPidSubEntries[] = {
    PID("container", PID_DIR_MODE, &g_procfsContainerVops, NULL),
    PID("fd", PID_DIR_MODE, &g_procfsFdVops, NULL),
    PID("meminfo", PID_FILE_MODE, &g_procfsPidMenInfoRead, &PROC_PID_MEMINFO_READ),
    PID("cpup", PID_FILE_MODE, &g_procfsPidCpupRead, &PROC_PID_CPUP_READ),
    PID("root", PID_LINK_MODE, &g_procfsPidRootVops, NULL),
#ifdef LOSCFG_USER_CONTAINER
    PID("uid_map", PID_FILE_MODE, &g_procfsComVops, &g_procUidMapOperations),
    PID("gid_map", PID_FILE_MODE, &g_procfsComVops, &g_procGidMapOperations),
#endif
#ifdef LOSCFG_TIME_CONTAINER
    PID("time_offsets", PID_FILE_MODE, &g_procfsPidTimeOffsetsVops, &g_procTimeOffsetsOperations),
#endif

};

static const struct ProcfsPidEntry procfsContainerSubEntries[] = {
    CONTAINER("mnt"),
    CONTAINER("pid_for_children"),
    CONTAINER("pid"),
    CONTAINER("user"),
    CONTAINER("uts"),
    CONTAINER("net"),
    CONTAINER("time_for_children"),
    CONTAINER("time"),
    CONTAINER("ipc"),
};

static void ProfsSetPidEntryVop(const char *name, struct Vnode **vpp, const struct ProcfsPidEntry *ppe, int arrSize)
{
    for (int i = 0; i < arrSize; i++) {
        if (!strncmp(name, ppe[i].name, ppe[i].len)) {
            (*vpp)->vop = ppe[i].vop;
            if (S_ISLNK(ppe[i].mode)) {
                (*vpp)->type = VNODE_TYPE_LNK;
            }
        }
    }

    return;
}

static int ProfsCreatePidSubEntries(struct Vnode *node, const struct ProcfsPidEntry *ppe, int arrSize)
{
    int result = 0;

    if (node == NULL) {
        return -EINVAL;
    }

    struct ProcDirEntry *entry = (struct ProcDirEntry *)(node->data);
    if (entry == NULL) {
        return -ENODATA;
    }

    for (int i = 0; i < arrSize; i++) {
        result = ProcfsCreatePidSubEntry(entry, ppe[i].name, ppe[i].mode, ppe[i].pfop);
        if (result != LOS_OK) {
            return result;
        }
    }

    return LOS_OK;
}

static int ProcfsPidLookup(struct Vnode *parent, const char *name, int len, struct Vnode **vpp)
{
    int result = 0;
    result = ProfsCreatePidSubEntries(parent, g_procfsPidSubEntries, ARRAY_SIZE(g_procfsPidSubEntries));
    if (result != LOS_OK) {
        return result;
    }

    result = VfsProcfsLookup(parent, name, len, vpp);
    if (result == LOS_OK) {
        ProfsSetPidEntryVop(name, vpp, g_procfsPidSubEntries, ARRAY_SIZE(g_procfsPidSubEntries));
    }

    return result;
}

static int ProcfsPidReaddir(struct Vnode *node, struct fs_dirent_s *dir)
{
    int result = 0;

    result = ProfsCreatePidSubEntries(node, g_procfsPidSubEntries, ARRAY_SIZE(g_procfsPidSubEntries));
    if (result != LOS_OK) {
        return result;
    }

    return VfsProcfsReaddir(node, dir);
}


static int ProcfsContainerLookup(struct Vnode *parent, const char *name, int len, struct Vnode **vpp)
{
    int result = 0;

    result = ProfsCreatePidSubEntries(parent, procfsContainerSubEntries, ARRAY_SIZE(procfsContainerSubEntries));
    if (result != LOS_OK) {
        return result;
    }

    result = VfsProcfsLookup(parent, name, len, vpp);
    if (result == LOS_OK) {
        ProfsSetPidEntryVop(name, vpp, procfsContainerSubEntries, ARRAY_SIZE(procfsContainerSubEntries));
        (*vpp)->type = VNODE_TYPE_NOFOLLOW_LNK;
    }

    return result;
}

static int ProcfsContainerReaddir(struct Vnode *node, struct fs_dirent_s *dir)
{
    int result = 0;

    result = ProfsCreatePidSubEntries(node, procfsContainerSubEntries, ARRAY_SIZE(procfsContainerSubEntries));
    if (result != LOS_OK) {
        return result;
    }

    return VfsProcfsReaddir(node, dir);
}

static ssize_t ProcfsContainerSubReadlink(struct Vnode *vnode, char *buffer, size_t bufLen)
{
    UINT32 vnum = 0;
    struct ProcDirEntry *entry = NULL;
    char containerName[CONTAINER_NAME_LEN + 1] = {0};
    ssize_t count = 0;

#ifdef LOSCFG_KERNEL_CONTAINER
    UINT32 pid = 0;
    LosProcessCB *processCB = NULL;
    ContainerBundle *ctnBundle = NULL;
#ifdef LOSCFG_USER_CONTAINER
    Credentials *credentials =NULL;
#endif
#endif
    entry = (struct ProcDirEntry *)vnode->data;
#ifdef LOSCFG_KERNEL_CONTAINER
    pid = atoi(entry->parent->parent->name);
#ifdef LOSCFG_PID_CONTAINER
    pid = GetGlobalPidByCurrPid(pid);
    if (pid == -LOS_NOK) {
        goto SET_CONTAINERNAME;
    }
#endif
    processCB = OS_PCB_FROM_PID(pid);
    ctnBundle = processCB->containerBundle;
#ifdef LOSCFG_USER_CONTAINER
    credentials = processCB->credentials;
#endif
    if (!strncmp(procfsContainerSubEntries[0].name, entry->name, entry->nameLen)) {
#ifdef LOSCFG_MNT_CONTAINER
        vnum = ctnBundle->mntContainer->containerBase.vnum;
#endif
    } else if (!strncmp(procfsContainerSubEntries[1].name, entry->name, entry->nameLen)
                && strlen(procfsContainerSubEntries[1].name) == entry->nameLen) {
#ifdef LOSCFG_PID_CONTAINER
        vnum = ctnBundle->pidContainerForChildren->containerBase.vnum;
#endif
    } else if (!strncmp(procfsContainerSubEntries[2].name, entry->name, entry->nameLen)) {
#ifdef LOSCFG_PID_CONTAINER
        vnum = ctnBundle->pidContainer->containerBase.vnum;
#endif
    } else if (!strncmp(procfsContainerSubEntries[3].name, entry->name, entry->nameLen)) {
#ifdef LOSCFG_USER_CONTAINER
        vnum = credentials->userContainer->containerBase.vnum;
#endif
    } else if (!strncmp(procfsContainerSubEntries[4].name, entry->name, entry->nameLen)) {
#ifdef LOSCFG_UTS_CONTAINER
        vnum = ctnBundle->utsContainer->containerBase.vnum;
#endif
    } else if (!strncmp(procfsContainerSubEntries[5].name, entry->name, entry->nameLen)) {
#ifdef LOSCFG_NET_CONTAINER
        vnum = ctnBundle->netContainer->containerBase.vnum;
#endif
    } else if (!strncmp(procfsContainerSubEntries[6].name, entry->name, entry->nameLen)
                && strlen(procfsContainerSubEntries[6].name) == entry->nameLen) {
#ifdef LOSCFG_TIME_CONTAINER
        vnum = ctnBundle->timeContainerForChildren->containerBase.vnum;
#endif
    } else if (!strncmp(procfsContainerSubEntries[7].name, entry->name, entry->nameLen)) {
#ifdef LOSCFG_TIME_CONTAINER
        vnum = ctnBundle->timeContainer->containerBase.vnum;
#endif
    } else if (!strncmp(procfsContainerSubEntries[8].name, entry->name, entry->nameLen)) {
#ifdef LOSCFG_IPC_CONTAINER
        vnum = ctnBundle->ipcContainer->containerBase.vnum;
#endif       
    }
#ifdef LOSCFG_PID_CONTAINER
SET_CONTAINERNAME:
#endif
#endif
    if (!strncmp(procfsContainerSubEntries[1].name, entry->name, entry->nameLen)) {
        snprintf(containerName, CONTAINER_NAME_LEN, "\'%s:[%u]\'", procfsContainerSubEntries[2].name, vnum);
    } else if (!strncmp(procfsContainerSubEntries[6].name, entry->name, entry->nameLen)) {
        snprintf(containerName, CONTAINER_NAME_LEN, "\'%s:[%u]\'", procfsContainerSubEntries[7].name, vnum);
    } else {
        snprintf(containerName, CONTAINER_NAME_LEN, "\'%s:[%u]\'", entry->name, vnum);
    }
    count = bufLen < strlen(containerName) ? bufLen : strlen(containerName);
    strncpy_s(buffer, bufLen, containerName, count);

    return count;
}

void *ProcfsContainerGet(struct Vnode *vnode)
{
#ifdef LOSCFG_KERNEL_CONTAINER
    struct ProcDirEntry *entry = (struct ProcDirEntry *)vnode->data;
    UINT32 pid = 0;
    LosProcessCB *processCB = NULL;
    ContainerBundle *containerBundle = NULL;
#ifdef LOSCFG_USER_CONTAINER
    Credentials *credentials = NULL;
#endif
    VOID *targetContainer = NULL;

    pid = atoi(entry->parent->parent->name);
#ifdef LOSCFG_PID_CONTAINER
    pid = GetGlobalPidByCurrPid(pid);
    if (pid == -LOS_NOK) {
        return NULL;
    }
#endif
    processCB = OS_PCB_FROM_PID(pid);
    containerBundle = processCB->containerBundle;
#ifdef LOSCFG_USER_CONTAINER
    credentials = processCB->credentials;
#endif

    if (!strncmp(procfsContainerSubEntries[0].name, entry->name, entry->nameLen)) {
#ifdef LOSCFG_MNT_CONTAINER
        targetContainer = (void*)containerBundle->mntContainer;
#endif
    } else if (!strncmp(procfsContainerSubEntries[1].name, entry->name, entry->nameLen)
                && strlen(procfsContainerSubEntries[1].name) == entry->nameLen) {
#ifdef LOSCFG_PID_CONTAINER
        targetContainer = (void*)containerBundle->pidContainerForChildren;
#endif
    } else if (!strncmp(procfsContainerSubEntries[2].name, entry->name, entry->nameLen)) {
#ifdef LOSCFG_PID_CONTAINER
        targetContainer = (void*)containerBundle->pidContainer;
#endif
    } else if (!strncmp(procfsContainerSubEntries[3].name, entry->name, entry->nameLen)) {
#ifdef LOSCFG_USER_CONTAINER
        targetContainer = (void*)credentials->userContainer;
#endif
    } else if (!strncmp(procfsContainerSubEntries[4].name, entry->name, entry->nameLen)) {
#ifdef LOSCFG_UTS_CONTAINER
        targetContainer = (void*)containerBundle->utsContainer;
#endif
    } else if (!strncmp(procfsContainerSubEntries[5].name, entry->name, entry->nameLen)) {
#ifdef LOSCFG_NET_CONTAINER
        targetContainer = (void*)containerBundle->netContainer;
#endif
    } else if (!strncmp(procfsContainerSubEntries[6].name, entry->name, entry->nameLen)
                && strlen(procfsContainerSubEntries[6].name) == entry->nameLen) {
#ifdef LOSCFG_TIME_CONTAINER
        targetContainer = (void*)containerBundle->timeContainerForChildren;
#endif
    } else if (!strncmp(procfsContainerSubEntries[7].name, entry->name, entry->nameLen)) {
#ifdef LOSCFG_TIME_CONTAINER
        targetContainer = (void*)containerBundle->timeContainer;
#endif
    } else if (!strncmp(procfsContainerSubEntries[8].name, entry->name, entry->nameLen)) {
#ifdef LOSCFG_IPC_CONTAINER
        targetContainer = (void*)containerBundle->ipcContainer;
#endif
    } else {
        PRINT_ERR("Error. entry->name is invalid as %s\n", entry->name);
        return NULL;
    }

    return targetContainer;
#else
    return NULL;
#endif
}

int ProcfsContainerTypeGet(struct Vnode *vnode)
{
    struct ProcDirEntry *entry = (struct ProcDirEntry *)vnode->data;
    int containerType = -1;

    if (!strncmp(procfsContainerSubEntries[0].name, entry->name, entry->nameLen)) {
#ifdef LOSCFG_MNT_CONTAINER
        containerType = CLONE_NEWNS;
#endif
    } else if (!strncmp(procfsContainerSubEntries[1].name, entry->name, entry->nameLen)
                && strlen(procfsContainerSubEntries[1].name) == entry->nameLen) {
#ifdef LOSCFG_PID_CONTAINER
        containerType = CLONE_NEWPID;
#endif
    } else if (!strncmp(procfsContainerSubEntries[2].name, entry->name, entry->nameLen)) {
#ifdef LOSCFG_PID_CONTAINER
        containerType = CLONE_NEWPID;
#endif
    } else if (!strncmp(procfsContainerSubEntries[3].name, entry->name, entry->nameLen)) {
        containerType = CLONE_NEWUSER;
    } else if (!strncmp(procfsContainerSubEntries[4].name, entry->name, entry->nameLen)) {
        containerType = CLONE_NEWUTS;
    } else if (!strncmp(procfsContainerSubEntries[5].name, entry->name, entry->nameLen)) {
        containerType = CLONE_NEWNET;
    } else if (!strncmp(procfsContainerSubEntries[6].name, entry->name, entry->nameLen)
                && strlen(procfsContainerSubEntries[6].name) == entry->nameLen) {
#ifdef LOSCFG_TIME_CONTAINER
        containerType = CLONE_NEWTIME;
#endif
    } else if (!strncmp(procfsContainerSubEntries[7].name, entry->name, entry->nameLen)) {
#ifdef LOSCFG_TIME_CONTAINER
        containerType = CLONE_NEWTIME;
#endif
    } else if (!strncmp(procfsContainerSubEntries[8].name, entry->name, entry->nameLen)) {
#ifdef LOSCFG_IPC_CONTAINER
        containerType = CLONE_NEWIPC;
#endif
    } else {
        PRINT_ERR("Error. entry->name is invalid as %s\n", entry->name);
        return -1;
    }

    return containerType;
}

static int ProfsCreateFdSubEntries(struct Vnode *node)
{
    UINT32 pid = 0;
    int fd;
    struct fd_table_s *fdt = NULL;
    char name[FD_NAME_LEN + 1] = {0};
    int result = 0;

    if (node == NULL) {
        return -EINVAL;
    }

    struct ProcDirEntry *entry = (struct ProcDirEntry *)(node->data);
    if (entry == NULL) {
        return -ENODATA;
    }

    pid = atoi(entry->parent->name);
    fdt = LOS_GetFdTable(pid);
    if ((fdt == NULL) || (fdt->proc_fds == NULL)) {
        return -ENODATA;
    }

    for (fd = fdt->max_fds; fd >= MIN_START_FD ; fd--) {
        if (FD_ISSET(fd, fdt->proc_fds)) {
            snprintf(name, FD_NAME_LEN, "%d", fd);

            ProcfsCreatePidSubEntry(entry, name, PID_LINK_MODE, NULL);
            if (result != LOS_OK) {
                return result;
            }
        }
    }

    return LOS_OK;
}

static int ProcfsFdGetVnodeType(struct Vnode *parent, const char *name, enum VnodeType *type)
{
    UINT32 pid = 0;
    int fd = 0;
    int sysFd = 0;

    struct ProcDirEntry *entry = (struct ProcDirEntry *)(parent->data);
    if ((entry == NULL) || (entry->parent == NULL)) {
        return -EINVAL;
    }

    pid = atoi(entry->parent->name);
    struct fd_table_s *fdt = LOS_GetFdTable(pid);
    if ((fdt == NULL) || (fdt->proc_fds == NULL)) {
        return -EINVAL;
    }

    struct filelist *fileList = sched_getfiles();
    fd = atoi(name);
    (void)sem_wait(&fileList->fl_sem);
    (void)sem_wait(&fdt->ft_sem);

    if (FD_ISSET(fd, fdt->proc_fds)) {
        sysFd = fdt->ft_fds[fd].sysFd;
        if (sysFd < CONFIG_NFILE_DESCRIPTORS) {
            *type = VNODE_TYPE_LNK;
        } else {
            *type = VNODE_TYPE_NOFOLLOW_LNK;
        }
    }
    (void)sem_post(&fdt->ft_sem);
    (void)sem_post(&fileList->fl_sem);

    return LOS_OK;
}

static int ProcfsFdLookup(struct Vnode *parent, const char *name, int len, struct Vnode **vpp)
{
    int result = 0;

    result = ProfsCreateFdSubEntries(parent);
    if (result != LOS_OK) {
        return result;
    }

    result = VfsProcfsLookup(parent, name, len, vpp);
    if (result == LOS_OK) {
        (*vpp)->vop = &g_procfsFdSubVops;
        result = ProcfsFdGetVnodeType(parent, name, &((*vpp)->type));
    }

    return result;
}

static int ProcfsFdReaddir(struct Vnode *node, struct fs_dirent_s *dir)
{
    int result = 0;

    result = ProfsCreateFdSubEntries(node);
    if (result != LOS_OK) {
        return result;
    }

    return VfsProcfsReaddir(node, dir);
}

static ssize_t ProcfsFdSubReadlink(struct Vnode *vnode, char *buffer, size_t bufLen)
{
    UINT32 pid = 0;
    int fd;
    int sysFd;
    char *name = NULL;
    struct file *filp = NULL;
    ssize_t count = 0;

    if (vnode == NULL) {
        return -EINVAL;
    }

    struct ProcDirEntry *entry = (struct ProcDirEntry *)(vnode->data);
    if (entry == NULL) {
        return -ENODATA;
    }

    pid = atoi(entry->parent->parent->name);
    struct fd_table_s *fdt = LOS_GetFdTable(pid);
    if ((fdt == NULL) || (fdt->proc_fds == NULL)) {
        return 0;
    }
    struct filelist *fileList = &tg_filelist;
    fd = atoi(entry->name);

    (void)sem_wait(&fileList->fl_sem);
    (void)sem_wait(&fdt->ft_sem);

    if (FD_ISSET(fd, fdt->proc_fds)) {
        sysFd = fdt->ft_fds[fd].sysFd;
        if (sysFd < CONFIG_NFILE_DESCRIPTORS) {
            filp = &fileList->fl_files[sysFd];
            name = filp->f_path;
        } else if (sysFd < (CONFIG_NFILE_DESCRIPTORS + CONFIG_NSOCKET_DESCRIPTORS)) {
            name = "(socks)";
        } else if (sysFd < (FD_SETSIZE + CONFIG_NTIME_DESCRIPTORS)) {
            name = "(timer)";
        } else if (sysFd < (MQUEUE_FD_OFFSET + CONFIG_NQUEUE_DESCRIPTORS)) {
            name = "(mqueue)";
        } else if (sysFd < (EPOLL_FD_OFFSET + CONFIG_EPOLL_DESCRIPTORS)) {
            name = "(epoll)";
        } else {
            name = "(unknown)";
        }

        count = bufLen < strlen(name) ? bufLen : strlen(name);
        strncpy_s(buffer, bufLen, name, count);
    }

    (void)sem_post(&fdt->ft_sem);
    (void)sem_post(&fileList->fl_sem);

    return count;
}

static ssize_t ProcfsPidRootReadlink(struct Vnode *vnode, char *buffer, size_t bufLen)
{
    UINT32 pid = 0;
    LosProcessCB *processCB = NULL;
    char *name = NULL;
    ssize_t count = 0;

    if (vnode == NULL) {
        return -EINVAL;
    }

    struct ProcDirEntry *entry = (struct ProcDirEntry *)(vnode->data);
    if (entry == NULL) {
        return -ENODATA;
    }

    pid = atoi(entry->parent->name);
    processCB = OS_PCB_FROM_PID(pid);
    name = processCB->files->rootVnode->filePath;
    count = bufLen < strlen(name) ? bufLen : strlen(name);
    strncpy_s(buffer, bufLen, name, count);

    return count;
}


struct VnodeOps g_procfsPidVops = {
    .Lookup = ProcfsPidLookup,
    .Getattr = VfsProcfsStat,
    .Readdir = ProcfsPidReaddir,
    .Opendir = VfsProcfsOpendir,
    .Closedir = VfsProcfsClosedir,
    .Truncate = VfsProcfsTruncate
};

static struct VnodeOps g_procfsContainerVops = {
    .Lookup = ProcfsContainerLookup,
    .Getattr = VfsProcfsStat,
    .Readdir = ProcfsContainerReaddir,
    .Opendir = VfsProcfsOpendir,
    .Closedir = VfsProcfsClosedir,
    .Truncate = VfsProcfsTruncate
};

static struct VnodeOps g_procfsContainerSubVops = {
    .Getattr = VfsProcfsStat,
    .Readlink = ProcfsContainerSubReadlink
};

#ifdef LOSCFG_USER_CONTAINER
static struct VnodeOps g_procfsComVops = {
    .Lookup = VfsProcfsLookup,
    .Getattr = VfsProcfsStat,
    .Readdir = VfsProcfsReaddir,
    .Opendir = VfsProcfsOpendir,
    .Closedir = VfsProcfsClosedir,
    .Truncate = VfsProcfsTruncate
};
#endif

static struct VnodeOps g_procfsPidMenInfoRead = {
    .Lookup = ProcfsContainerLookup,
    .Getattr = VfsProcfsStat,
    .Readdir = ProcfsContainerReaddir,
    .Opendir = VfsProcfsOpendir,
    .Closedir = VfsProcfsClosedir,
    .Truncate = VfsProcfsTruncate
};

static struct VnodeOps g_procfsPidCpupRead = {
    .Lookup = ProcfsContainerLookup,
    .Getattr = VfsProcfsStat,
    .Readdir = ProcfsContainerReaddir,
    .Opendir = VfsProcfsOpendir,
    .Closedir = VfsProcfsClosedir,
    .Truncate = VfsProcfsTruncate
};

static struct VnodeOps g_procfsFdVops = {
    .Lookup = ProcfsFdLookup,
    .Getattr = VfsProcfsStat,
    .Readdir = ProcfsFdReaddir,
    .Opendir = VfsProcfsOpendir,
    .Closedir = VfsProcfsClosedir,
    .Truncate = VfsProcfsTruncate
};

static struct VnodeOps g_procfsFdSubVops = {
    .Getattr = VfsProcfsStat,
    .Readlink = ProcfsFdSubReadlink
};

static struct VnodeOps g_procfsPidRootVops = {
    .Getattr = VfsProcfsStat,
    .Readlink = ProcfsPidRootReadlink
};

#ifdef LOSCFG_TIME_CONTAINER
static struct VnodeOps g_procfsPidTimeOffsetsVops = {
    .Lookup = VfsProcfsLookup,
    .Getattr = VfsProcfsStat,
    .Readdir = VfsProcfsReaddir,
    .Opendir = VfsProcfsOpendir,
    .Closedir = VfsProcfsClosedir,
    .Truncate = VfsProcfsTruncate
};
#endif

